package com.wimcorp.magic.client;

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import org.fusesource.jansi.Ansi.Color;

import com.wimcorp.magic.client.input.AbilitySelector;
import com.wimcorp.magic.client.input.BooleanSelector;
import com.wimcorp.magic.client.input.CardSelector;
import com.wimcorp.magic.client.input.ColorSelector;
import com.wimcorp.magic.client.input.ConjureManaSelector;
import com.wimcorp.magic.client.input.ConsoleInput;
import com.wimcorp.magic.client.input.DeckSelector;
import com.wimcorp.magic.client.input.DeclareAttackSelector;
import com.wimcorp.magic.client.input.DeclareBlockerSelector;
import com.wimcorp.magic.client.input.MainSelector;
import com.wimcorp.magic.client.input.MessageSelector;
import com.wimcorp.magic.client.input.OrderCardsSelector;
import com.wimcorp.magic.client.input.ReadySelector;
import com.wimcorp.magic.input.Location;
import com.wimcorp.magic.input.Target;
import com.wimcorp.magic.input.Type;
import com.wimcorp.magic.userinterface.GameInterface;
import com.wimcorp.magic.userinterface.UserInterface;

public class CommandLineInterface extends UnicastRemoteObject implements UserInterface {
	private final ConsoleInput mainConsole = new ConsoleInput();

	private String playerName;
	private GameInterface game;

	public CommandLineInterface(String playerName, GameInterface game) throws RemoteException {
		this.playerName = playerName;
		this.game = game;
	}

	public void start() {
		try {
			ExecutorService executor = Executors.newSingleThreadExecutor();
			Callable<Void> callable = new MainSelector(this);
			executor.submit(callable);
			executor.shutdown();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public void selectDeck() throws RemoteException {
		new DeckSelector(this).call();
	}

	public void confirmReady() throws RemoteException {
		new ReadySelector(this).call();
	}

	@Override
	public long select(final Target target, final Set<String> types, final Set<String> locations,
			final Set<String> behaviors) throws RemoteException {
		try {
			ExecutorService executor = Executors.newSingleThreadExecutor();
			Callable<Long> callable = new CardSelector(this, target, types, locations, behaviors);
			Future<Long> future = executor.submit(callable);
			executor.shutdown();
			return future.get();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return -1;
	}

	@Override
	public long select(Target target, Type type, Location location) throws RemoteException {
		Set<String> types = new HashSet<>();
		types.add(type.toString());
		Set<String> locations = new HashSet<>();
		locations.add(location.toString());
		Set<String> behaviors = new HashSet<>();
		return select(target, types, locations, behaviors);
	}

	@Override
	public boolean selectBoolean(final String message) throws RemoteException {
		try {
			ExecutorService executor = Executors.newSingleThreadExecutor();
			Callable<Boolean> callable = new BooleanSelector(this, message);
			Future<Boolean> future = executor.submit(callable);
			executor.shutdown();
			return future.get();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return false;
	}
	
	@Override
	public void conjureMana(String message) throws RemoteException {
		try {
			ExecutorService executor = Executors.newSingleThreadExecutor();
			Callable<Void> callable = new ConjureManaSelector(this,message);
			Future<Void> future = executor.submit(callable);
			executor.shutdown();
			future.get();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@Override
	public int selectColor(final List<String> colorOptions) throws RemoteException {
		try {
			ExecutorService executor = Executors.newSingleThreadExecutor();
			Callable<Integer> callable = new ColorSelector(this, colorOptions);
			Future<Integer> future = executor.submit(callable);
			executor.shutdown();
			return future.get();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return -1;
	}

	@Override
	public Long[] order(final String message, final long[] input) throws RemoteException {
		try {
			ExecutorService executor = Executors.newSingleThreadExecutor();
			Callable<Long[]> callable = new OrderCardsSelector(this, message, input);
			Future<Long[]> future = executor.submit(callable);
			executor.shutdown();
			return future.get();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return new Long[0];

	}

	@Override
	public long castAbility() {
		try {
			ExecutorService executor = Executors.newSingleThreadExecutor();
			Set<String> types = new HashSet<>();
			types.add(Type.CARD.toString());
			Set<String> locations = new HashSet<>();
			locations.add(Location.BATTLEFIELD.toString());
			Callable<Long> callable = new AbilitySelector(this);
			Future<Long> future = executor.submit(callable);
			executor.shutdown();
			return future.get();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return 0;
	}

	@Override
	public Long[] declareAttackers() {
		try {
			boolean attack = selectBoolean("Would you like to attack? ");
			if (attack) {
				ExecutorService executor = Executors.newSingleThreadExecutor();
				Callable<Long[]> callable = new DeclareAttackSelector(this);
				Future<Long[]> future = executor.submit(callable);
				Long[] attackers = future.get();
				executor.shutdown();
				return attackers;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return new Long[0];
	}

	@Override
	public Map<Long,Long[]> declareBlockers(Set<Long> attackerIds) {
		Map<Long,Long[]> result = new HashMap<>();
		try {
			for (long attackerId : attackerIds) {
				String attacker = game.getOpponentCardName(playerName, attackerId);
				if (game.isBlockable(playerName, attackerId)) {
					boolean block = selectBoolean("Would you like to block " + attacker + "? ");
					if (block) {
						ExecutorService executor = Executors.newSingleThreadExecutor();
						Callable<Long[]> callable = new DeclareBlockerSelector(this, attackerId);
						Future<Long[]> future = executor.submit(callable);
						Long[] blockers = future.get();
						result.put(attackerId, blockers);
						executor.shutdown();
					}
				} else {
					message(String.format("You don't have creatures that can block %s. ",attacker));
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return result;
	}

	@Override
	public void discardExtraCard() {
		try {
			ExecutorService executor = Executors.newSingleThreadExecutor();
			Set<String> types = new HashSet<>();
			Set<String> locations = new HashSet<>();
			locations.add(Location.HAND.toString());
			Set<String> behaviors = new HashSet<>();
			Callable<Long> callable = new CardSelector(this,"You have more then 7 cards. Select a card to discard. ", Target.SELF, types, locations, behaviors);
			Future<Long> future = executor.submit(callable);
			executor.shutdown();
			long cardId = future.get();
			if (cardId != -1) {
				game.discardCard(playerName, cardId);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public void message(final String message) throws RemoteException {
		print(message, Color.WHITE);
		
		/*try {
			ExecutorService executor = Executors.newSingleThreadExecutor();
			Callable<Void> callable = new MessageSelector(this,message);
			executor.submit(callable);
			executor.shutdown();
		} catch (Exception e) {
			e.printStackTrace();
		}*/
		
	}

	public void printInstruction(String message) {
		print(message + "<type -h for help>", Color.WHITE);
	}

	public void print(String message, Color color) {
		/*
		 * AnsiConsole.systemInstall(); System.out.println(new
		 * Ansi().render("@|"+ color +" "+message+"|@"));
		 * AnsiConsole.systemUninstall();
		 */
		System.out.println(message);

	}

	public ConsoleInput getMainConsole() {
		return mainConsole;
	}

	public String getPlayerName() {
		return playerName;
	}

	public GameInterface getGame() {
		return game;
	}

	

}
